UIWebView / WKWebView / SFSafariViewController から、自分自身を起動する Universal Links を踏んだ場合の違和感あるUXへの対処
1 はじめに
Universal Linksを使用すると、https://〜のリンクからインストールされているアプリを起動することが可能です。
Universal Linksを試してみました。関連づけファイル(apple-app-site-association)はS3に置きました。
これは、Webベージや、メールで送ったリンクからのアプリ誘導には非常に有効な手段です。
しかし、起動されるべきアプリが既に実行中であり、そのアプリの中で使用しているUIWebView等で、自分自身を起動するUniversal Linksを踏んだ場合どうなるのでしょう?
っと言うことで、調べみました。
2 試験環境
(1) 試験用ページ
試験のために次の2つのページを用意しました。
- アクセス元のページ
- アクセス先のページ
「アクセス元のページ」には、「アクセス先のページ」(Universal Links用のapple-app-site-associationが設置してある)へのリンクが置かれています。
こちらは、「アクセス先のページ」です。上のページでリンクを選択すると、ここに遷移します。
(2) テストアプリ
テストのために、Universal Linksで起動するテストアプリを作成しましました。
トップには、「UIWebView」「WKWebView」「SFSafariViewController」の3つのボタンがあり、それぞれのビューを開いて、先の「アクセス元のページ」を開くようになっています。
また、Universal Linksで起動された場合は、下記のように、ログ表示するとともに、別のビューを開いてURLを表示するようにしました。
func application(_ application: UIApplication, continue userActivity: NSUserActivity, restorationHandler: @escaping ([Any]?) -> Void) -> Bool { if userActivity.activityType == NSUserActivityTypeBrowsingWeb { print("application(_:continue:restorationandler:)") let url = (userActivity.webpageURL?.absoluteString)! print("URL=\(url)") viewController.openUniversalLinksView(url: url) } return true }
下図は、テストアプリを実行している様子です。 最初に、ボタンをタップして、各Webビューを表示しています。そして、一旦アプリを終了させ、SafariでUniversal Linksを選択することでアプリを起動しています。
3 動作確認
(1) UIWebView
UIWebViewからUniversalLinksのリンクをタップすると「アクセス先のページ」が表示されてしまいます。(目的のビューは表示されない)
アプリは既に起動しているので、「Universal Linksは、問題ない」とも言えますが、これは、意図した動作ではないかも知れません。
(2) WKWebView
WKWebViewでも同じく「アクセス先のページ」が表示されてしまいます。(目的のビューは表示されない)
(3) SFSafariViewController
SFSafariViewControllerの場合は、リンクをタップしても、表示は変わりませんでした。(目的のビューは表示されない) しかし、application(_:continue:restorationHandler:)を通過したログが表示されていました。
SFSafariViewControllerは、テストアプリとは、別のプロセスとして動作しているため、バックグランドになっているアプリがUniversal Linksでキックされたいう理解で良いのでしょうか?
しかし、「完了」でSFSafariViewControllerを閉じても、目的のビューには遷移していませんでした。
これは、表示されていたログです。
application(_:continue:restorationHandler:) URL=https://universal-links-bucket.s3.amazonaws.com/index.html
4 対応要領の一例
動作確認の結果、どのWebViewも、予定した動作ができていないと言えそうです。(目的のビューは表示されない) アプリの仕様によって、対策は色々あると思いますが、今回は、その一例を考えてみました。
(1) UIWebViewの対応
UIWebViewでは、UIWebViewDelegateプロトコルを実装することでwebView(_:shouldStartLoadWith:navigationType:)で遷移前に処理をトラップすることが可能です。
ここで遷移先を確認して、対象URLだった場合は、UIWebViewを表示しているビューをクローズして、目的のビューを表示しました。
func webView(_ webView: UIWebView, shouldStartLoadWith request: URLRequest, navigationType: UIWebViewNavigationType) -> Bool { let url = request.url?.absoluteString if url == DestinationUrl { // UniversalLinksで起動された場合の処理 let viewController = navigationController?.viewControllers.first as! ViewController viewController.openUniversalLinksView(url: url!) // NavigationControllerで戻る _ = navigationController?.popViewController(animated: true) // 遷移をキャンセル return false } return true }
(2) WKWebViewの対応
WKWebViewの場合も、先の対応と同じです。webView(_:decidePolicyFor:decisionHandler:)でトラップして、同じ要領で目的のビューに遷移しています。
func webView(_ webView: WKWebView, decidePolicyFor navigationAction: WKNavigationAction, decisionHandler: @escaping (WKNavigationActionPolicy) -> Void) { let url = navigationAction.request.url?.absoluteString if url == DestinationUrl { // UniversalLinksで起動された場合の処理 let viewController = navigationController?.viewControllers.first as! ViewController viewController.openUniversalLinksView(url: url!) // NavigationControllerで戻る _ = navigationController?.popViewController(animated: true) // 遷移をキャンセル decisionHandler(.cancel) return } decisionHandler(.allow) }
(3) SFSafariViewControllerの対応
SFSafariViewControllerでは、遷移先に移動する前にトラップする処理を書けませんので、application(_:continue:restorationHandler:)側から、対処を考えてみました。
Universal Linksで起動されて、目的のビューに遷移する際に、もしSFSafariViewControllerが表示されていたらクロースする処理を追加しました。
func openUniversalLinksView (url: String){ // もし、SFSafariViewControllerが表示されていたらクローズする sfview?.dismiss(animated: true, completion: nil) self.url = url performSegue(withIdentifier: "toUniversalLinks",sender: nil) }
(4) 動作確認
対応したテストアプリを実行している様子です。 SFSafariViewControllerから遷移する場合に、一旦トップのビューが表示されてしまうのは、SFSafariViewControllerを閉じてしまわないと、画面遷移ができなかったためです。適切な方法が有りましたら、是非教えてやって下さい。
5 最後に
今回は、Universal Linksを自らが踏んだ場合の対策を検討してみました。
ここで紹介した対策は一例に過ぎません。例えば、今回のテストアプリでは、UINavigationControllerでWeb表示のビューに遷移しているので、popViewController(animated:)を使用しましたが、Modalで遷移している場合などは、dismiss(animated:completion:)で、ビューを閉じる必要があるかも知れません。
どちらにしても、UXに違和感を与えないためには、何らかの処置が必要だと思いました。
参考のため、テストアプリ(対策は終わっています)を置きました。
再利用する場合は、Universal Linksのドメイン及びapple-app-site-associationについては、別途、準備が必要です。
[GitHub] https://github.com/furuya02/ULSample
6 参考リンク
Apple Refelence Support Universal Links
Apple プログラミングリファレンス ユニバーサルリンクへの対応
Universal Linksを試してみました。関連づけファイル(apple-app-site-association)はS3に置きました。
[iOS] ディープリンク(Custom URL Scheme)でアプリを起動する